#pragma once

#ifndef WINVER
#define WINVER 0x0501
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0410
#endif
#ifndef _WIN32_IE
#define _WIN32_IE 0x0600
#endif
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <mswsock.h>
#include <ws2tcpip.h>
typedef void(__stdcall*ser_fun)(HANDLE n_cli, SOCKET * n_ser, int n_type, char* n_data, ULONG n_size);

extern HANDLE g_iocp;
extern bool g_open;
extern DWORD g_ser_thread;

#define GlobalFrees(x){if(x !=NULL){GlobalFree(x);x = NULL;}}
#define CloseHandles(x){if(x !=NULL){CloseHandle(x);x = NULL;}}

void int2char(char* data, int val);
void closesockets(SOCKET x);
HANDLE g_iocp = NULL;
bool g_open = true;
DWORD g_ser_thread = 0;
void int2char(char* data, int val)
{
    	wsprintfA(data, "%d", val);
}
void closesockets(SOCKET x) {
    	BOOL bDontLinger = FALSE;
        	setsockopt(x, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(BOOL));
            	



                	linger m_sLinger = {1,0};
                    	setsockopt(x, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(linger));
                        	closesocket(x);
                            	x = INVALID_SOCKET;
}
}
}
class client;

typedef struct tcpop
{
    	OVERLAPPED	op;
        	client		*so;
            	int			state;
                	char		*buf;
                    	DWORD		slen;
                        	DWORD		cb;
                            	DWORD		bufSize;
                                	DWORD		bufOffset;
                                    	ULONG		m_so;
}S_tcpop, *P_tcpop;

class client {
    public:
    	bool cli_connect(ser_fun n_fun, char* n_ip, USHORT n_port, int n_modle, int n_time, int n_blen, DWORD n_buflen,
        		int n_proxy, char* n_proxyip, USHORT n_proxyport, char* n_pacc, char* n_pass, bool ipv6);
                	bool cli_connect_fun(P_tcpop op);
                    	bool cli_SoRecv(P_tcpop op);
                        	bool cli_recv(P_tcpop op);
                            	bool cli_send(char* n_buf, DWORD n_len);
                                	bool cli_close();
                                    	bool cli_socks4(PCHAR host, WORD port, PCHAR username, PCHAR userpass);
                                        	bool cli_socks5(PCHAR host, WORD port, PCHAR username, PCHAR userpass);
                                            	bool cli_http(PCHAR host, WORD port, PCHAR username, PCHAR userpass);
                                                	char cli_b2c(BYTE b);
                                                    	int cli_base64(LPBYTE srcbuf, int cbsrc, LPBYTE dstbuf, int cbdst);
                                                        public:
                                                        	SOCKET m_so;
                                                            	int m_modle;
                                                                	int m_blen;
                                                                    	DWORD m_buflen;
                                                                        	ser_fun m_fun;
                                                                            	bool m_close;
                                                                                	int m_record_int;
                                                                                    	char* m_record_char;
};
}

#include "stdafx.h"
#include "BBTCP.h"
#include "stdafx.h"
#include "BBTCP.h"

bool client::cli_connect(ser_fun n_fun, char* n_ip, USHORT n_port, int n_modle, int n_time, int n_blen, DWORD n_buflen,
	int n_proxy, char* n_proxyip, USHORT n_proxyport, char* n_pacc, char* n_pass, bool ipv6) {
        	m_fun = n_fun;
            	m_modle = n_modle;
                	m_blen = n_blen;
                    	m_buflen = n_buflen;
                        	m_close = false;
                            	m_record_int = 0;
                                	m_record_char = NULL;
                                    	m_so = WSASocket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
                                        	if (INVALID_SOCKET == m_so) {
                                                		return false;
                                            }
                                            	
                                                	HANDLE n_iocp = CreateIoCompletionPort((HANDLE)m_so, g_iocp, NULL, 0);
                                                    	if (n_iocp != g_iocp) {
                                                            		closesockets(m_so);
                                                                    		return false;
                                                        }
                                                        	
                                                            	
                                                                	DWORD to = 40960;
                                                                    	setsockopt(m_so, SOL_SOCKET, SO_RCVTIMEO, (char*)&to, sizeof(to));
                                                                        	setsockopt(m_so, SOL_SOCKET, SO_SNDTIMEO, (char*)&to, sizeof(to));

                                                                            	unsigned long ul = 1;
                                                                                	ioctlsocket(m_so, FIONBIO, (unsigned long *)&ul);

                                                                                    	if (ipv6)
                                                                                        	{
                                                                                                		struct addrinfo hints, *AddrInfo = NULL;
                                                                                                        		memset(&hints, 0, sizeof(hints));

                                                                                                                		hints.ai_family = AF_INET6;
                                                                                                                        		hints.ai_socktype = SOCK_STREAM;
                                                                                                                                		hints.ai_protocol = IPPROTO_TCP;
                                                                                                                                        		hints.ai_flags = AI_PASSIVE;
                                                                                                                                                		char nport[4];
                                                                                                                                                        		int2char(nport, n_port);
                                                                                                                                                                		if (getaddrinfo(n_ip, nport, &hints, &AddrInfo) != 0) {
                                                                                                                                                                            			closesockets(m_so);
                                                                                                                                                                                        			return NULL;
                                                                                                                                                                        }
                                                                                                                                                                        		connect(m_so, AddrInfo->ai_addr, AddrInfo->ai_addrlen);
                                                                                            } 
                                                                                            	else
                                                                                                	{
                                                                                                        		sockaddr_in sin;
                                                                                                                		sin.sin_family = AF_INET;
                                                                                                                        		sin.sin_addr.S_un.S_addr = 0;
                                                                                                                                		sin.sin_port = 0;
                                                                                                                                        		if (SOCKET_ERROR == bind(m_so, (SOCKADDR *)&sin, 16)) {
                                                                                                                                                    			closesockets(m_so);
                                                                                                                                                                			return false;
                                                                                                                                                }
                                                                                                                                                		hostent *pHost = gethostbyname(0!=n_proxy?n_proxyip:n_ip);
                                                                                                                                                        		if (NULL != pHost) {
                                                                                                                                                                    			WriteProcessMemory(INVALID_HANDLE_VALUE, &sin.sin_addr.S_un.S_addr, pHost->h_addr_list[0], pHost->h_length, NULL);
                                                                                                                                                                                			sin.sin_addr.S_un.S_addr = inet_addr(inet_ntoa(sin.sin_addr));
                                                                                                                                                                }
                                                                                                                                                                		else {
                                                                                                                                                                            			sin.sin_addr.S_un.S_addr = inet_addr(0!=n_proxy?n_proxyip:n_ip);
                                                                                                                                                                        }
                                                                                                                                                                        		sin.sin_port = htons(0!=n_proxy?n_proxyport:n_port);

                                                                                                                                                                                		connect(m_so, (sockaddr*)&sin, sizeof(sockaddr));
                                                                                                    }
                                                                                                    	
                                                                                                        	struct timeval timeout;
                                                                                                            	fd_set r;
                                                                                                                	FD_ZERO(&r);
                                                                                                                    	FD_SET(m_so, &r);
                                                                                                                        	timeout.tv_sec = n_time;
                                                                                                                            	timeout.tv_usec = 0;
                                                                                                                                	int ret = select((int)m_so, 0, &r, 0, &timeout);
                                                                                                                                    	if (ret <= 0) {
                                                                                                                                            		closesockets(m_so);
                                                                                                                                                    		return false;
                                                                                                                                        }

                                                                                                                                        	if (0 != n_proxy) {
                                                                                                                                                		bool ret = false;
                                                                                                                                                        		switch (n_proxy) {
                                                                                                                                                                    		case 1:	ret = cli_socks4(n_ip, n_port, n_pacc, n_pass); break;
                                                                                                                                                                            		case 2:	ret = cli_socks5(n_ip, n_port, n_pacc, n_pass); break;
                                                                                                                                                                                    		case 3:	ret = cli_http(n_ip, n_port, n_pacc, n_pass); break;
                                                                                                                                                                                            		default:
                                                                                                                                                                                                    			closesockets(m_so);
                                                                                                                                                                                                                			return false;
                                                                                                                                                                }

                                                                                                                                                                		if (!ret) {
                                                                                                                                                                            			closesockets(m_so);
                                                                                                                                                                                        			return false;
                                                                                                                                                                        }
                                                                                                                                            }
                                                                                                                                            	tcpop *op = (tcpop*)GlobalAlloc(GMEM_ZEROINIT, sizeof(tcpop));
                                                                                                                                                	if (NULL == op) {
                                                                                                                                                        		closesockets(m_so);
                                                                                                                                                                		return false;
                                                                                                                                                    }
                                                                                                                                                    	op->so = this;
                                                                                                                                                        	op->state = 1;
                                                                                                                                                            	op->buf = NULL;
                                                                                                                                                                	op->bufSize = 0;
                                                                                                                                                                    	op->bufOffset = 0;
                                                                                                                                                                        	op->m_so = m_so;
                                                                                                                                                                            	
                                                                                                                                                                                	
                                                                                                                                                                                    	PostQueuedCompletionStatus(g_iocp, 0, NULL, (LPOVERLAPPED)op);

                                                                                                                                                                                        	return true;
    }

    bool client::cli_connect_fun(P_tcpop op)
    {
        	setsockopt(m_so, SOL_SOCKET, 0x7010, NULL, 0);
            	GlobalFrees(op->buf);
                	if (m_modle)
                    	{
                            		op->buf = (char*)GlobalAlloc(GMEM_ZEROINIT, 4);
                                    		if (NULL == op->buf) {
                                                			return false;
                                            }
                                            		op->bufSize = sizeof(DWORD);
                        }
                        	else
                            	{
                                    		op->buf = (char*)GlobalAlloc(GMEM_ZEROINIT, m_blen);
                                            		if (NULL == op->buf) {
                                                        			return false;
                                                    }
                                                    		op->bufSize = m_blen;
                                }
                                	op->state = 2;
                                    	op->bufOffset = 0;
                                        	op->so = this;
                                            	WSABUF wsabuf;
                                                	wsabuf.buf = op->buf + op->bufOffset;
                                                    	wsabuf.len = op->bufSize - op->bufOffset;

                                                        	DWORD Flg = 0;
                                                            	if (WSARecv(m_so, &wsabuf, 1, &op->cb, &Flg, (LPWSAOVERLAPPED)op, NULL))
                                                                	{
                                                                        		int ercode = WSAGetLastError();
                                                                                		if (ercode != WSA_IO_PENDING) {
                                                                                            			return false;
                                                                                        }
                                                                    }
                                                                    	return true;
    }
    bool client::cli_SoRecv(P_tcpop op) {
        	op->so = this;
            	op->state = 2;
                	WSABUF wsabuf;
                    	wsabuf.buf = op->buf + op->bufOffset;
                        	wsabuf.len = op->bufSize - op->bufOffset;

                            	DWORD Flg = 0;
                                	if (WSARecv(m_so, &wsabuf, 1, &op->cb, &Flg, (LPWSAOVERLAPPED)op, NULL)) {
                                        		int ercode = WSAGetLastError();
                                                		if (ercode != WSA_IO_PENDING && ercode != WSAEFAULT) {
                                                            			return false;
                                                        }
                                    }
                                    	return true;
    }
    bool client::cli_recv(P_tcpop op) {
        	if (1 == m_modle) {
                		op->bufOffset += op->cb; //更新大小

                        		if (op->bufOffset < op->bufSize) {
                                    			return cli_SoRecv(op);
                                }

                                		DWORD size = *((DWORD*)(op->buf));

                                        		if (size > m_buflen || size <= 0) {
                                                    			return false;
                                                }

                                                		if (op->bufOffset < 4 + size) {
                                                            			GlobalFrees(op->buf);
                                                                        			op->buf = (char*)GlobalAlloc(GMEM_ZEROINIT, 4 + size);
                                                                                    			if (NULL == op->buf) {
                                                                                                    				return false;
                                                                                                }
                                                                                                			op->bufSize = 4+size;
                                                                                                            			op->bufOffset = 4;
                                                                                                                        			WriteProcessMemory(INVALID_HANDLE_VALUE, op->buf, &size, 4, NULL);
                                                                                                                                    			return cli_SoRecv(op);
                                                        }

                                                        		if (op->bufSize - 4 > 0) {
                                                                    			if (NULL != m_fun) {
                                                                                    				m_fun(this, &m_so, 2, op->buf + 4, op->bufSize - 4);
                                                                                }
                                                                                			
                                                                }

                                                                		GlobalFrees(op->buf);
                                                                        		op->buf = (char*)GlobalAlloc(GMEM_ZEROINIT, 4);
                                                                                		if (NULL == op->buf) {
                                                                                            			return false;
                                                                                        }
                                                                                        		op->bufSize = 4;

            } else {
                		if (NULL != m_fun) {
                            			m_fun(this, &m)
                        }
            }
                                                                                        }
                                                                                }
                                                                }
                                                                                                }
                                                        }
                                                }
                                }
            }
    }
                                                        }
                                    }
    }
                                                                                        }
                                                                    }
                                                    }
                                }
                                            }
                        }
    }
                                                                                                                                                    }
                                                                                                                                                                        }
                                                                                                                                                                }
                                                                                                                                            }
                                                                                                                                        }
                                                                                                                                                                        }
                                                                                                                                                                }
                                                                                                                                                }
                                                                                                    }
                                                                                                                                                                        }
                                                                                            }
                                                        }
                                            }
    }